[Android][Kotlin] KotlinでAndroidアプリケーション その3 [Fragment]
こむろです。
独自のFragmentクラスを実装すると共に継承などを確認します。
準備
環境は以下の通り
- OS: Mac OSX Yosemite 10.10.3
- IDE: Android Studio 1.2
前回、前々回と何度もやっていますので、Androidプロジェクトの作成は軽めに流します。
プロジェクトの作成
プロジェクトパッケージ名やパスを適当に作成します。Androidのバージョンは5.0以降にしました。
プロジェクトテンプレートはNavigation Drawerを利用したものを選択してみましょう。
FinishボタンでAndroidプロジェクトのひな形が完成しました。
Kotlin用のプロジェクトへコンバートする
作成したAndroidプロジェクトをKotlin用に変換します。Android Studioには以下のプラグインがインストール済みであるという前提です。導入していない場合は、前々回の記事を参考にしてインストールしてください。
javaパッケージをkotlinパッケージへリネームします。
警告が出ますが、そのまま気にせず実行します。
Code>Convert Java File to Kotlin Fileを実行します。
MainActivityとNavigationDrawerFragmentがKotlinに変換されていることが確認できると思います。
build.gradleにKotlinファイルのパスを追加します。Tools>Kotlin>Configure Kotlin in Projectを選択します。
どのモジュールにKotlinファイルがあるか、プラグインバージョンなどを聞かれるので、今回はそのままの設定でOKを実行します。
正常に実行されるとKotlinファイルがAndroidのモジュールとして認識され、ビルドできるようになります。以下のようになっていれば正常に設定の追加が完了しています。
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' android { compileSdkVersion 22 buildToolsVersion "22.0.1" defaultConfig { applicationId "kotlin.android.classmethod.jp.blogkotlin" minSdkVersion 21 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main.java.srcDirs += 'src/main/kotlin' } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:support-v4:22.1.1' compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" } buildscript { ext.kotlin_version = '0.11.91.4' repositories { mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } repositories { mavenCentral() }
これでAndroidプロジェクトをKotlinで記述する準備が整いました。
MainActivityのエラーを解消する
MainActivity.ktファイルを開くと分かりますが、いくつかエラーが出ています。
super.と記述された継承元クラスのメソッド呼び出しの箇所でエラーが起きています。これを解消しましょう。以下のように記述し直します。
override fun onCreateOptionsMenu(menu: Menu?): Boolean { if (!mNavigationDrawerFragment!!.isDrawerOpen()) { // Only show items in the action bar relevant to this screen // if the drawer is not showing. Otherwise, let the drawer // decide what to show in the action bar. getMenuInflater().inflate(R.menu.main, menu) restoreActionBar() return true } return super<Activity>.onCreateOptionsMenu(menu) } override fun onOptionsItemSelected(item: MenuItem?): Boolean { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. val id = item!!.getItemId() //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true } return super<Activity>.onOptionsItemSelected(item) }
super<Activity>.
と指定し直します。
これはTraitを利用した多重継承を許可しているKotlinならではの特徴です。superだと、どの継承元を指しているかの曖昧さが残ってしまうため、明確にクラス名を指定する必要があります。superでエラーが出た場合は、このあたりの曖昧さを疑ったほうが良さそうです。
Fragmentクラスを作成する
準備
独自のFragmentクラスを実装します。まずはクラスを作成。
適当なクラス名を記載し、KindはClassを選択します。
継承
Fragmentクラスを継承します。デフォルトコンストラクタの呼び出しも必要です。
public class SelectFragment: Fragment() { }
デフォルトコンストラクタをここで呼びたくない場合、Javaのように明示的にコンストラクタを定義したい場合は次のように書けば良いようです。
public class SelectFragment: Fragment { public constructor() : super() { } }
コンストラクタの定義を複数持ちたい場合などは、下の方法が有効ですね。
セカンダリコンストラクタをこのように指定するようです。もしかするとプライマリコンストラクタとしては、super()が呼ばれないデフォルトコンストラクタになってるかもしれません(要調査)
Layoutファイルをひもづける
いつもと同じようにonCreateView()で、LayoutのXMLを読み込みましょう。
Layoutファイルは以下の様なものを用意しました。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ff0000"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Select Fragment" android:padding="10dip" android:textSize="18sp" android:textColor="#ffffff" android:id="@+id/textView" /> </LinearLayout>
ちょっと目が痛い配色ですが、このLayoutファイルを適用します。FragmentクラスのonCreateViewをoverrideします。
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_select, container, false) }
staticメソッド
Fragmentクラスでは、よくファクトリメソッドとしてnewInstance()やgetInstance()などを定義します。ここでもそれに倣い、staticメソッドを定義してみます。
なんとなくJavaっぽい感じで書いてみましたが、そんなものはない、とエラーで怒られています。
staticメソッドのようにClass.methodなどでアクセスしたい場合は、以下のように記述すれば良いようです。
public class SelectFragment: Fragment() { companion object { fun newInstance(): SelectFragment { val fragment = SelectFragment() return fragment } } }
結果を確認してみましょう。
意図通りの呼び出しができるようになりました。
MainActivityのメニューからFragment切り替え
さて、Fragmentクラスの実装も終わったので、全て結合してみます。折角NavigationDrawerを利用しているので、メニュー1を元々のFragment、メニュー2を今回作成したFragmentに割り当てます。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity$PlaceholderFragment" android:background="#ffff00"> <TextView android:id="@+id/section_label" android:layout_width="wrap_content" android:text="@string/app_name" android:layout_height="wrap_content" /> </RelativeLayout>
起動時に表示されるFragmentのLayout。こちらもちょっと目が痛い配色。
NavigationDrawerの選択動作の実装は、MainActivityが継承しているNavigationDrawerFragment.NavigationDrawerCallbacksインターフェース(KotlinだとTrait?)のonNavigationDrawerItemSelected()に記述されています。こちらを書き換えましょう。
override fun onNavigationDrawerItemSelected(position: Int) { // update the main content by replacing fragments val fragmentManager = getFragmentManager() when(position) { 0 -> { fragmentManager.beginTransaction().replace(R.id.container, PlaceholderFragment.newInstance(position + 1)).commit() } 1 -> { fragmentManager.beginTransaction().replace(R.id.container, SelectFragment.newInstance()).commit() } } }
Kotlinにおいては、switchに相当する構文は、whenとなります。ここでは単なる数字のマッチングしか行っていませんが、パターンマッチングなど柔軟な処理が行えるようです。さらに型推論を利用して分岐させることも可能なので、別々の型を混在させて判断させることが可能です。Javaで同じようなことをやろうとすると、if(hoge instanceof Hoge)などを駆使する形になるのではないでしょうか。
実行すると、NavigationDrawerでFragmentが切り替わるのを確認出来ると思います。これでFragmentの大まかな実装は出来ました。
Kotlinを選んだ理由
こんな理由です。割と個人的な好みが大きい気がします。あとTwitterのTLで見かけることが多いのも理由 *1です。
- AndroidにJava8がいつ来るのか分からない。Java7のサポートも一部だけ。
- なんとなくIntelliJを使っていたので
- Groovyよりもなんか馴染みが良かった
サーバーサイドで、Java8を触ってしまってから「あー、この機能が使えたら良いのになぁ」と思うことが多かったので、Alternative Java言語としてKotlinを選択しました。しばらくこの言語でもりもり書いてみようと思います。